package cz.vutbr.fit.xzemko01.tfmcbs.diagram.extensions.validators;

import java.util.ArrayList;
import java.util.List;

import cz.vutbr.fit.xzemko01.tfmcbs.model.Component;
import cz.vutbr.fit.xzemko01.tfmcbs.model.Container;
import cz.vutbr.fit.xzemko01.tfmcbs.model.Entity;
import cz.vutbr.fit.xzemko01.tfmcbs.model.Generalization;

public class GeneralizationLinkTargetEndValidator {
	public static boolean validate(Container container, Generalization generalization, Entity entityChild, Entity entityParent) {
		if(entityChild == null || entityParent == null) {
			return false;
		}
		
		return validateGeneralizedEntitiesHasSameType(entityChild, entityParent)
				&& validateEntityHasUniqueExtends(entityChild, entityParent)
				&& validateEntityIsNotInCyclicGeneralization(entityChild, entityParent);
	}
	
	private static boolean validateGeneralizedEntitiesHasSameType(Entity entityChild, Entity entityParent) {
		return entityChild.getClass() == entityParent.getClass()
			|| (entityChild instanceof Component && entityParent instanceof Component);			
	}
	
	private static boolean validateEntityHasUniqueExtends(Entity entityChild, Entity entityParent) {
		List<Generalization> extendsList = entityChild.getExtends();
		if(extendsList == null) {
			return true;
		}
		
		for(Generalization generalization : extendsList) {
			if(generalization.getParent() == entityParent) {
				return false;
			}
		}
		return true;
	}
	
	private static boolean validateEntityIsNotInCyclicGeneralization(Entity entityChild, Entity entityParent) {
		List<Entity> foundEntities = new ArrayList<Entity>();
		foundEntities.add(entityParent);
		return !isDuplicationInEntityExtending(foundEntities, entityChild);
	}
	
	private static boolean isDuplicationInEntityExtending(List<Entity> foundEntities, Entity rootNode) {
		if(rootNode == null) {
			return false;
		}
		if(foundEntities.contains(rootNode)) {
			return true;
		}
		
		List<Generalization> childGeneralizations = rootNode.getChilds();
		if(childGeneralizations == null) {
			return false;
		}
		
		foundEntities.add(rootNode);
		
		boolean result = false;
		for(Generalization generalization : childGeneralizations) {
			Entity child = generalization.getChild();
			result = isDuplicationInEntityExtending(foundEntities, child);
			if(result) {
				break;
			}
		}
		foundEntities.remove(rootNode);
		return result;
	}
}
